﻿using Framework.Zhaogang.Log.Client.ConfigSection;
using Framework.Zhaogang.Utility.Core.Configuration;
using Framework.Zhaogang.Utility.Core.IO;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace Framework.Zhaogang.Log.Client
{
    /// <summary>
    /// 日志队列
    /// </summary>
    class LogQueue
    {

        #region Ctors

        static LogQueue()
        {
            AppLogQueue = new ConcurrentQueue<ExpandoObject>();
            appLogTask = Task.Run(() => WriteAppLog());
        }

        #endregion

        #region Fields

        /// <summary>
        /// 应用程序日志任务
        /// </summary>
        static Task appLogTask;

        #endregion

        #region Properties

        /// <summary>
        /// 线程安全的应用程序日志队列
        /// </summary>
        public static ConcurrentQueue<ExpandoObject> AppLogQueue { get; private set; }

        #endregion

        #region Private Methods

        /// <summary>
        /// 写入应用程序日志
        /// </summary>
        static void WriteAppLog()
        {
            while (true)
            {
                var logDeQueueList = new List<ExpandoObject>();
                var logQueueCount = AppLogQueue.Count;
                if (logQueueCount > 0 && AppLogQueue.Count <= ConfigurationSectionHandler<LogSettings>.Settings.QueueLimit)
                {
                    for (var i = 0; i < logQueueCount; i++)
                    {
                        var log = new ExpandoObject();
                        if (AppLogQueue.TryDequeue(out log))
                        {
                            logDeQueueList.Add(log);
                        }
                    }
                }
                if (logDeQueueList.Count > 0)
                {
                    SendLogs(logDeQueueList);
                }
                System.Threading.Thread.Sleep(ConfigurationSectionHandler<LogSettings>.Settings.WriteLogInterval);
            }
        }

        /// <summary>
        /// 日志分组
        /// </summary>
        /// <param name="logs"></param>
        /// <returns></returns>
        static List<List<ExpandoObject>> LogGrouping(List<ExpandoObject> logs)
        {
            var logGroupCount = ConfigurationSectionHandler<LogSettings>.Settings.LogGroupCount;
            var group = new List<List<ExpandoObject>>();
            var count = logs.Count();
            var groupListCount = (int)Math.Ceiling((decimal)count / logGroupCount);
            for (var i = 0; i < groupListCount; i++)
            {
                var groupItem = new List<ExpandoObject>();
                for (var y = 0; y < logGroupCount; y++)
                {
                    var index = y + logGroupCount * i;
                    if (index < logs.Count)
                    {
                        groupItem.Add(logs[index]);
                    }
                    else
                    {
                        break;
                    }
                }
                group.Add(groupItem);
            }
            return group;
        }

        /// <summary>
        /// 发送日志
        /// </summary>
        /// <param name="logs"></param>
        static void SendLogs(List<ExpandoObject> logs)
        {
            var logGroup = LogGrouping(logs);
            var streamContents = GetStreamContent(logGroup);
            if (logGroup.Count == streamContents.Count && logGroup.Count > 0)
            {
                Task.Run(() =>
                {
                    Parallel.ForEach(streamContents, streamContent =>
                    {
                        new HttpClient().PostAsync(ConfigurationSectionHandler<LogSettings>.Settings.Url, streamContent);
                    });
                });
            }
        }

        /// <summary>
        /// 获取日志流集合
        /// </summary>
        /// <param name="logGroup"></param>
        /// <returns></returns>
        private static List<StreamContent> GetStreamContent(List<List<ExpandoObject>> logGroup)
        {
            var streamContents = new List<StreamContent>();
            logGroup.ForEach(g =>
            {
                try
                {
                    streamContents.Add(new StreamContent(Compression.GZipCompress(g)));
                }
                catch { }
            });
            return streamContents;
        }

        #endregion

    }
}
